package com.share.handler;

import com.share.common.enums.HttpStatusEnum;
import com.share.exception.CustomerAuthenticationException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 认证异常处理工具类
 */
@Slf4j
@Component
public class AuthenticationExceptionHandler {

    private final Map<Class<? extends AuthenticationException>, AuthenticationFailureInfo> failureInfoMap;

    public AuthenticationExceptionHandler() {
        this.failureInfoMap = new HashMap<>();
        initializeFailureInfoMap();
    }

    private void initializeFailureInfoMap() {
        // 账号相关异常
        failureInfoMap.put(AccountExpiredException.class,
                new AuthenticationFailureInfo("账号已过期", HttpStatusEnum.UNAUTHORIZED, "ACCOUNT_EXPIRED"));
        failureInfoMap.put(CredentialsExpiredException.class,
                new AuthenticationFailureInfo("密码已过期", HttpStatusEnum.UNAUTHORIZED, "CREDENTIALS_EXPIRED"));
        failureInfoMap.put(DisabledException.class,
                new AuthenticationFailureInfo("账号已被禁用", HttpStatusEnum.FORBIDDEN, "ACCOUNT_DISABLED"));
        failureInfoMap.put(LockedException.class,
                new AuthenticationFailureInfo("账号已被锁定", HttpStatusEnum.FORBIDDEN, "ACCOUNT_LOCKED"));

        // 认证相关异常
        failureInfoMap.put(BadCredentialsException.class,
                new AuthenticationFailureInfo("用户名或密码错误", HttpStatusEnum.UNAUTHORIZED, "INVALID_CREDENTIALS"));
        failureInfoMap.put(UsernameNotFoundException.class,
                new AuthenticationFailureInfo("账号不存在", HttpStatusEnum.NOT_FOUND, "USER_NOT_FOUND"));
        failureInfoMap.put(InternalAuthenticationServiceException.class,
                new AuthenticationFailureInfo("认证服务异常", HttpStatusEnum.INTERNAL_SERVER_ERROR, "AUTH_SERVICE_ERROR"));

        // 自定义认证异常
        failureInfoMap.put(CustomerAuthenticationException.class,
                new AuthenticationFailureInfo(HttpStatusEnum.UNAUTHORIZED.getMessage(), HttpStatusEnum.UNAUTHORIZED, "CUSTOM_AUTH_ERROR"));

    }

    public AuthenticationFailureInfo getFailureInfo(AuthenticationException exception) {
        return failureInfoMap.getOrDefault(exception.getClass(),
                new AuthenticationFailureInfo("认证失败", HttpStatusEnum.INTERNAL_SERVER_ERROR, "UNKNOWN_ERROR"));
    }

    public void logAuthenticationFailure(HttpServletRequest request,
                                         AuthenticationException exception,
                                         String username) {
        try {
            // 获取认证信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String actualUsername = username;

            // 如果传入的用户名为空，尝试从认证信息中获取
            if (actualUsername == null && authentication != null && authentication.getPrincipal() != null) {
                actualUsername = authentication.getPrincipal().toString();
            }

            // 获取会话信息
            HttpSession session = request.getSession(false);
            String sessionId = session != null ? session.getId() : null;

            // 获取认证详情
            WebAuthenticationDetails details = null;
            if (authentication != null) {
                details = (WebAuthenticationDetails) authentication.getDetails();
            }

            // 获取IP地址
            String remoteAddress = details != null ? details.getRemoteAddress() :
                    request.getHeader("X-Forwarded-For") != null ?
                            request.getHeader("X-Forwarded-For") :
                            request.getRemoteAddr();

            // 获取请求信息
            String requestPath = request.getRequestURI();
            String requestMethod = request.getMethod();

            // 记录日志
            log.warn("认证失败 - IP: {}, 会话ID: {}, 用户名: {}, 请求路径: {}, 请求方法: {}, 异常类型: {}, 异常信息: {}",
                    remoteAddress,
                    sessionId,
                    actualUsername,
                    requestPath,
                    requestMethod,
                    exception.getClass().getSimpleName(),
                    exception.getMessage());

            // 如果是认证失败，记录失败次数
            if (session != null && actualUsername != null) {
                recordLoginFailure(session, actualUsername);
            }
        } catch (Exception e) {
            // 记录日志时发生异常，只记录基本信息
            log.error("记录认证失败日志时发生异常: {}", e.getMessage());
            log.warn("认证失败 - IP: {}, 异常类型: {}, 异常信息: {}",
                    request.getRemoteAddr(),
                    exception.getClass().getSimpleName(),
                    exception.getMessage());
        }
    }

    private void recordLoginFailure(HttpSession session, String username) {
        try {
            String failureKey = "login_failure_" + username;
            Integer failureCount = (Integer) session.getAttribute(failureKey);
            if (failureCount == null) {
                failureCount = 0;
            }
            failureCount++;
            session.setAttribute(failureKey, failureCount);

            // 如果失败次数过多，可以在这里添加额外的处理逻辑
            if (failureCount >= 5) {
                log.warn("用户 {} 登录失败次数过多，可能需要锁定账号", username);
            }
        } catch (Exception e) {
            log.error("记录登录失败次数时发生异常: {}", e.getMessage());
        }
    }

    @Getter
    @AllArgsConstructor
    public static class AuthenticationFailureInfo {
        private final String message;
        private final HttpStatusEnum status;
        private final String errorCode;
    }
}